Приоритет операторов
====================

.. index::
  приоритет операторов
  цепочки операторов
  ассоциативность

Как мы уже говорили на протяжении всей книги, выражения можно объединять в цепочку с
более чем одним оператором. Например, следующая строка содержит четыре
выражений, соединенных тремя операторами:

.. code-block:: d

    a = b + c * d // three operators: =, +, and *

Правила старшинства операторов определяют порядок, в котором выполняются цепочки операторов
и выражения, которые они используют. Операторы выполняются в соответствии
в соответствии с их старшинством: сначала старшие, затем младшие.
Ниже приведена таблица старшинства операторов в D. Операторы перечислены от
от самого высокого старшинства к самому низкому. Операторы, находящиеся в одной строке таблицы, имеют
одинаковый приоритет. (Обертка строк внутри ячеек таблицы не имеет значения; например,
== и !is имеют одинаковый приоритет.) Если не указано иное, операторы
левоассоциативными.
Некоторые термины, используемые в таблице, объясняются ниже.

.. list-table:: Title
   :widths: 25 25 50
   :header-rows: 1
   :align: center

   * - Оператор
     - Описание
     - Замечания
   * - !
     - Template instantiation
     - не могут быть связаны в цепочку
   * - =>
     - Определение lambda выражения
     - TODO
   * - . ++ -- ( [
     - postfix операции
     - скобки болжны быть сбалансированы
   * - ^^
     - возведение в степень
     - ассоциативность справа
   * - ++ -- * + - ! & ~ cast
     - унарные опрерации
     -
   * - \* / %
     - бинарные операции
     -
   * - + - ~
     - бинарные операции
     -
   * - >> << >>>
     - операции сдвига
     -
   * - == != > < >= <= in !in is !is
     - операции сравнения
     - Неупорядоченные по битовым операторам, не могут быть связаны в цепочку
   * - &
     - побитовое И
     - Неупорядоченные по операторам сравнения (TODO)
   * - ^
     - побитовое исключающие или
     - Неупорядоченные по операторам сравнения
   * - |
     - побитовое или
     - Неупорядоченные по операторам сравнения
   * - &&
     - логическое И
     - Short-circuit
   * - ||
     - логическое ИЛИ
     - Short-circuit
   * - ?:
     - тернарный опрератор
     - правая ассоциативность
   * - = -= += = \*= %= ^= ^^= ~= <<= >>= >>>=
     - операторы присваивания
     - правая ассоциативность
   * - =>
     - определение лямбда выражение
     - TODO
   * - ,
     - запятая
     -
   * - \.\.
     - диапазон
     - виртуальный оператор, добавляется в соответствии с синтаксисом




**Цепочка операций**

Рассмотрим строку из начала главы:

.. code-block

    a = b + c * d

Поскольку двоичное * имеет больший приоритет, чем двоичное +, а двоичное + имеет больший
чем =, это выражение выполняется как следующий эквивалент в скобках
эквивалент:

.. code-block:: d

    a = (b + (c * d)) // сначала *, потом +, потом =

В качестве другого примера, поскольку постфикс . имеет более высокий приоритет, чем унарный \*, следующее выражение
следующее выражение сначала получит доступ к члену ptr объекта o, а затем
разыменовать его:

.. code-block:: d

    *o.ptr // ← dereferences pointer member o.ptr
    *(o.ptr) // ← equivalent of the above
    (*o).ptr // ← NOT equivalent of the above

некоторые операции не могут быть связаны в цепочку

.. code-block:: d

    if (a > b == c) // ← compilation ERROR
    {
    // ...
    }

.. code-block:: console

    Error: found '==' when expecting ')'

Программист должен указать желаемый порядок выполнения в круглых скобках:

.. code-block:: d

    if ((a > b) == c)  // ← compiles
    {
    // ...
    }

**Ассоциативность**

Если два оператора имеют одинаковый приоритет, то их ассоциативность определяет.
какой оператор выполняется первым: тот, что слева, или тот, что справа.
Большинство операторов левоассоциативны; первым выполняется тот, который находится слева:

.. code-block:: d

    10 - 7 - 3;
    (10 - 7) - 3; // ← equivalent of the above (== 0)
    10 - (7 - 3); // ← NOT equivalent of the above (== 6)

Некоторые операторы являются правоассоциативными; тот, который находится в правой части, выполняется
первым:

.. code-block:: d

    4 ^^ 3 ^^ 2;
    4 ^^ (3 ^^ 2); // ← equivalent of the above (== 262144)
    (4 ^^ 3) ^^ 2; // ← NOT equivalent of the above (== 4096)

**Неупорядоченные группы операторов**

Приоритет между побитовыми операторами и логическими операторами не определен
языком:

.. code-block:: d

    if (a & b == c)  // ← compilation ERROR
    {
    // ...
    }

.. code-block:: console

    Error: b == c must be parenthesized when next to operator &

Программист должен указать желаемый порядок выполнения в круглых скобках:

.. code-block:: d

    if ((a & b) == c) // ← compiles
    {
    // ...
    }

**The precedence of =>**

Хотя => не является оператором, он участвует в таблице дважды, чтобы указать, как он
взаимодействует с операторами в левой и правой частях.

.. code-block:: d

    l = a => a = 1;

Хотя обе стороны => выше содержат оператор =, => имеет приоритет перед = с левой стороны.
левой стороны, поэтому он сильнее привязывается к a, как если бы программист написал
следующие круглые скобки:

.. code-block:: d

    l = (a => a = 1);

В правой части => имеет меньший приоритет, чем =, поэтому a в правой части
привязывается к = сильнее, как если бы был указан следующий дополнительный набор круглых скобок:

.. code-block:: d

    l = (a => (a = 1));

В результате лямбда-выражение становится не просто a => a, а включает в себя
остальную часть выражения: a => a = 1, что означает, что при заданном a произвести a = 1.
Затем эта лямбда присваивается переменной l.
*Примечание: Это просто пример; в противном случае a = 1 не является осмысленным телом для
лямбды, потому что мутация параметраа как бы теряется, и результатом
вызова лямбды всегда будет 1. (Причина, по которой я говорю "кажется", заключается в том, что оператор присваивания
может быть перегружен для типаa и может иметь побочные эффекты.)*

**Оператор запятой**

Оператор запятой - это бинарный оператор. Он выполняет сначала выражение левой части
затем правую. Значения обоих выражений
игнорируются.

.. code-block:: d

    int a = 1;
    foo(), bar(), ++a;
    assert(a == 2);

Оператор запятой чаще всего используется в циклах for, когда цикл
итерация включает в себя изменение более чем одной переменной:

.. code-block:: d

    for ({ int i; int j; } i < 10; ++i, ++j)
    {
    // ...
    }
